package manager

import (
	"crypto/rand"
	"crypto/x509/pkix"
	"encoding/asn1"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"net"
	"time"

	"github.com/gmsm/sm2"
)

// CertificateManager - 证书管理
type CertificateManager struct {
}

// Signature - 签名函数
func (manager *CertificateManager) Signature(data []byte) ([]byte, error) {
	privKey, err := sm2.ReadPrivateKeyFromPem(StoreConfig.StoreDirectory+"priv.pem", nil)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	// 读取公钥
	_, err = sm2.ReadPublicKeyFromPem(StoreConfig.StoreDirectory+"pub.pem", nil)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	// 签名
	signdata, err := privKey.Sign(rand.Reader, data, nil)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	return signdata, nil
}

// GenerateClientCert function.
func (manager *CertificateManager) GenerateClientCert(_commonName string, _organization string, pubKeyData []byte) ([]byte, error) {
	// 读取系统密钥
	privKey, err := sm2.ReadPrivateKeyFromPem(StoreConfig.SystemPrvKey, []byte(StoreConfig.SystemPassword))
	if err != nil {
		log.Fatal(err)
	}

	testExtKeyUsage := []sm2.ExtKeyUsage{sm2.ExtKeyUsageClientAuth, sm2.ExtKeyUsageServerAuth}
	testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}}
	extraExtensionData := []byte("extra extension")
	subjectPublicKeyInfo := string(pubKeyData)
	commonName := _commonName
	template := sm2.Certificate{
		// SerialNumber is negative to ensure that negative
		// values are parsed. This is due to the prevalence of
		// buggy code that produces certificates with negative
		// serial numbers.
		SerialNumber: big.NewInt(-1),
		Subject: pkix.Name{
			CommonName:   commonName,
			Organization: []string{_organization},
			Country:      []string{"China"},
			ExtraNames: []pkix.AttributeTypeAndValue{
				{
					Type:  []int{2, 5, 4, 42},
					Value: "Gopher",
				},
				// This should override the Country, above.
				{
					Type:  []int{2, 5, 4, 6},
					Value: "NL",
				},
			},
		},
		NotBefore: time.Unix(1000, 0),
		NotAfter:  time.Unix(100000, 0),

		//		SignatureAlgorithm: ECDSAWithSHA256,
		SignatureAlgorithm: sm2.SM2WithSM3,

		SubjectKeyId: []byte{1, 2, 3, 4},
		KeyUsage:     sm2.KeyUsageCertSign,

		ExtKeyUsage:        testExtKeyUsage,
		UnknownExtKeyUsage: testUnknownExtKeyUsage,
		PublicKey:          subjectPublicKeyInfo,

		BasicConstraintsValid: true,
		IsCA: true,

		OCSPServer:            []string{"http://ocsp.example.com"},
		IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"},

		DNSNames:       []string{"test.example.com"},
		EmailAddresses: []string{"gopher@golang.org"},
		IPAddresses:    []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")},

		PolicyIdentifiers:   []asn1.ObjectIdentifier{[]int{1, 2, 3}},
		PermittedDNSDomains: []string{".example.com", "example.com"},

		CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},

		ExtraExtensions: []pkix.Extension{
			{
				Id:    []int{1, 2, 3, 4},
				Value: extraExtensionData,
			},
			// This extension should override the SubjectKeyId, above.
			{
				// Id:       oidExtensionSubjectKeyId,
				Id:       []int{2, 5, 29, 14},
				Critical: false,
				Value:    []byte{0x04, 0x04, 4, 3, 2, 1},
			},
		},
	}

	pubKey, _ := privKey.Public().(*sm2.PublicKey)
	cert, err := sm2.CreateCertificateToMem(&template, &template, pubKey, privKey)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	return cert, nil
}

// CheckSystemSignature cert.
func (manager *CertificateManager) CheckSystemSignature(certData []byte) error {
	cert, err := sm2.ReadCertificateFromMem(certData)
	if err != nil {
		return err
	}

	return cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature)
}

// GetSystemPublicKey function
func (manager *CertificateManager) GetSystemPublicKey() []byte {
	dat, err := ioutil.ReadFile(StoreConfig.SystemPubKey)
	if err != nil {
		fmt.Println(err.Error())
		return nil
	}

	return dat
}
